﻿// This code is released under the BSD license.
namespace PugLib.UI
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;

    /// <summary>
    /// Tries to emulate the Task Dialog. Form will be called when the call to
    /// Task Dialog is not supported.
    /// </summary>
    internal partial class EmulateTaskDialog : PugForm
    {
        private readonly EmulateCommandLink _defaultButton;
        private readonly TaskDialog _taskDialog;
        private readonly Dictionary<int, EmulateCommandLink> _taskDialogButtons = new Dictionary<int, EmulateCommandLink>();

        internal EmulateTaskDialog(TaskDialog newTaskDialog)
        {
            Font = SystemFonts.MessageBoxFont;

            InitializeComponent();

            _taskDialog = newTaskDialog;

            BuildForm();

            // Only can set focus after everything has been build.
            if (_taskDialog.DefaultButton != 0)
            {
                // Set the default button.
                if (_taskDialogButtons.TryGetValue(_taskDialog.DefaultButton, out _defaultButton))
                {
                    _defaultButton.Select();
                }
                else
                {
                    if (flpSubAreaButtons.Controls.Count > 0)
                    {
                        // Select left-most button.
                        flpSubAreaButtons.Controls[flpSubAreaButtons.Controls.Count - 1].Select();
                    }
                }
            }
            else
            {
                if (flpSubAreaButtons.Controls.Count > 0)
                {
                    // Set the left-most button to be the default. Dictionary does
                    // not keep the order of the button that is added in, so this
                    // work around is used.
                    //
                    // Layout right to left, left-most item is at the end of the list.
                    flpSubAreaButtons.Controls[flpSubAreaButtons.Controls.Count - 1].Select();
                }
            }
        }

        internal int TaskDialogResult { get; private set; }


        /// <summary>
        /// Creates a standard button with a common event handler to close the
        /// the form and return the button's Id.
        /// </summary>
        /// <param name="tag">The button's Id.</param>
        /// <param name="text">The button's name.</param>
        /// <returns></returns>
        private EmulateCommandLink BuildControlButton(int tag, string text)
        {
            EmulateCommandLink button = new EmulateCommandLink();
            button.Click += button_Click;
            button.Tag = tag;
            button.Text = text;

            _taskDialogButtons.Add(tag, button);

            return button;
        }

        /// <summary>
        /// Main entry method to build the form.
        /// </summary>
        private void BuildForm()
        {
            ControlBox = false;
            StartPosition = _taskDialog.PositionRelativeToWindow ? FormStartPosition.CenterParent : FormStartPosition.CenterScreen;
            Text = String.IsNullOrEmpty(_taskDialog.WindowTitle) ? string.Empty : _taskDialog.WindowTitle;

            BuildFormMainArea();
            BuildFormFooterArea();
            // Build the sub area last because of the possibility of having the
            // expanded information in the footer. Then building the "expand/
            // collapse" button will be accurate.
            BuildFormSubArea();

            SetFormHeight();
        }

        private void BuildFormFooterArea()
        {
            tlpFooterArea.AutoSize = false;
            tlpFooterArea.Height = 0;
            tlpFooterArea.Visible = false;

            tlp2.Height = 0;
            tlp2.Visible = false;

            flpFooterExpandedInformationText.AutoSize = false;
            flpFooterExpandedInformationText.Height = 0;
            flpFooterExpandedInformationText.Visible = false;

            tlp3.Height = 0;
            tlp3.Visible = false;
        }

        /// <summary>
        /// Creates a standard link label and will convert any anchor links to
        /// a proper link.
        /// </summary>
        /// <param name="margin">The margin of the link label.</param>
        /// <param name="text">The text of the link label.</param>
        /// <returns></returns>
        private static Label BuildControlLinkLabel(Padding margin, string text)
        {
            Label linkLabel = new Label { AutoSize = true, Margin = margin, Text = text };
            return linkLabel;
        }

        private void BuildFormMainArea()
        {
            switch (_taskDialog.MainIcon)
            {
                case TaskDialogIcon.Error:
                    pbMainImage.Image = SystemIcons.Error.ToBitmap();
                    break;
                case TaskDialogIcon.Information:
                    pbMainImage.Image = SystemIcons.Information.ToBitmap();
                    break;
                case TaskDialogIcon.Shield:
                    pbMainImage.Image = SystemIcons.Shield.ToBitmap();
                    break;
                case TaskDialogIcon.Warning:
                    pbMainImage.Image = SystemIcons.Warning.ToBitmap();
                    break;
                default:
                    tlpMain.ColumnStyles[0].Width = 0;
                    break;
            }

            if (!String.IsNullOrEmpty(_taskDialog.MainInstruction))
            {
                Label label = new Label
                                  {
                                      AutoSize = true,
                                      Font = new Font(Font.FontFamily, (float)10.25, FontStyle.Bold),
                                      ForeColor = Color.DarkBlue,
                                      Margin = new Padding(0, 0, 0, 15),
                                      Text = _taskDialog.MainInstruction
                                  };

                flpMainControls.Controls.Add(label);
            }

            if (!String.IsNullOrEmpty(_taskDialog.Content))
            {
                flpMainControls.Controls.Add(BuildControlLinkLabel(new Padding(0, 0, 0, 15), _taskDialog.Content));
            }

            // Command links.
            if (_taskDialog.UseCommandLinks)
            {
                for (int i = 0; i < _taskDialog.Buttons.Length; i++)
                {
                    EmulateCommandLink cmdLink = new EmulateCommandLink();
                    cmdLink.Click += button_Click;
                    cmdLink.Tag = _taskDialog.Buttons[i].ButtonId;
                    cmdLink.Text = _taskDialog.Buttons[i].ButtonText;
                    cmdLink.Size = new Size(flpMainControls.Width - 10, cmdLink.BestHeight);

                    _taskDialogButtons.Add(_taskDialog.Buttons[i].ButtonId, cmdLink);

                    flpMainControls.Controls.Add(cmdLink);
                }
            }
        }


        private void BuildFormSubArea()
        {
            // Right side of the sub control area.
            // If there is no button, by default there will be an "Okay" button.
            if (_taskDialog.CommonButtons.Equals(TaskDialogCommonButtons.None) &&
                _taskDialog.Buttons.Length == 0)
            {
                flpSubAreaButtons.Controls.Add(BuildControlButton((int)DialogResult.OK, DialogResult.OK.ToString()));
            }
            else
            {
                int requiredTotalButtonsWidth = 0;

                TaskDialogCommonButtons[] commonButtons = (TaskDialogCommonButtons[])Enum.GetValues(typeof(TaskDialogCommonButtons));

                // Iterate through all the common buttons. Get by reverse order
                // as the layout direction is right to left.
                for (int i = commonButtons.Length - 1; i >= 0; i--)
                {
                    // There is no "None" button.
                    if (!commonButtons[i].Equals(TaskDialogCommonButtons.None))
                    {
                        // Now to check which button is needed.
                        if ((_taskDialog.CommonButtons & commonButtons[i]).Equals(commonButtons[i]))
                        {
                            // TaskDialogCommonButtons enums is not the same as
                            // DialogResult enums. Need to use another way of
                            // getting the correct DialogResult value.
                            Button button;

                            // DialogResult does not contain a "Close" enum,
                            // return value by TaskDialog is int 8.
                            if (commonButtons[i].Equals(TaskDialogCommonButtons.Close))
                            {
                                button = BuildControlButton(8, TaskDialogCommonButtons.Close.ToString());
                            }
                            // No "Ok" button. Is it a typo for TaskDialogCommonButtons in TaskDialog.cs?
                            else if (commonButtons[i].Equals(TaskDialogCommonButtons.Ok))
                            {
                                button = BuildControlButton((int)DialogResult.OK, DialogResult.OK.ToString());
                            }
                            else
                            {
                                DialogResult result = (DialogResult)Enum.Parse(typeof(DialogResult), commonButtons[i].ToString());
                                button = BuildControlButton((int)result, result.ToString());
                            }

                            button.TabIndex = i + _taskDialog.Buttons.Length;

                            flpSubAreaButtons.Controls.Add(button);

                            requiredTotalButtonsWidth += button.Width + button.Margin.Left + button.Margin.Right;
                        }
                    }
                }

                // Custom buttons. Get by reverse order as the layout direction
                // is right to left.
                if (!_taskDialog.UseCommandLinks)
                {
                    for (int i = _taskDialog.Buttons.Length - 1; i >= 0; i--)
                    {
                        Button button = BuildControlButton(_taskDialog.Buttons[i].ButtonId, _taskDialog.Buttons[i].ButtonText);
                        flpSubAreaButtons.Controls.Add(button);

                        button.TabIndex = i;

                        requiredTotalButtonsWidth += button.Width + button.Margin.Left + button.Margin.Right;
                    }
                }

                // Padding on the right.
                requiredTotalButtonsWidth += 15;

                if (requiredTotalButtonsWidth > flpSubAreaButtons.Width)
                {
                    Width += requiredTotalButtonsWidth - flpSubAreaButtons.Width;
                }
            }
        }

        /// <summary>
        /// Closes the form and populate the necessary return results.
        /// </summary>
        private void CloseForm()
        {
            Close();
        }


        private void SetFormHeight()
        {
            Height = (tlpMain.Height + 36)
                     + tlp1.Height // Separator.
                     + tlpSubArea.Height
                     + tlp2.Height // Separator.
                     + ((tlpFooterArea.Height > 0) ? tlpFooterArea.Height : 0)
                     + tlp3.Height // Separator.
                // When empty, default height is 6. TableLayoutPanel refuses to
                // shrink to 0 as there are items in it.
                     + ((tlpFooterExpandedInformationArea.Height > 6) ? tlpFooterExpandedInformationArea.Height : 0);
        }

        /// <summary>
        /// This will return the formatted contents of the form. Useful when
        /// the user press "ctrl + c" on the form.
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendLine("[Window Title]");
            sb.AppendLine(Text);

            if (!String.IsNullOrEmpty(_taskDialog.MainInstruction))
            {
                sb.AppendLine(string.Empty);
                sb.AppendLine("[Main Instruction]");
                sb.AppendLine(_taskDialog.MainInstruction);
            }

            if (!String.IsNullOrEmpty(_taskDialog.Content))
            {
                sb.AppendLine(string.Empty);
                sb.AppendLine("[Content]");
                sb.AppendLine(_taskDialog.Content);
            }

            // There should be a max of two controls here.
            foreach (Control control in flpSubAreaControls.Controls)
            {
                CheckBox checkBox = control as CheckBox;
                if (checkBox != null)
                {
                    sb.Append(checkBox.Checked ? "[X] " : "[ ] ");
                    sb.Append(control.Text);
                    sb.Append("  ");
                }
            }

            // Should contains only buttons. As the layout direction is right
            // to left, need to get by reverse order. Dictionary does not keep
            // the order of buttons added in. So this work around is used.
            for (int i = flpSubAreaButtons.Controls.Count - 1; i >= 0; i--)
            {
                Control control = flpSubAreaButtons.Controls[i];
                if (control is Button)
                {
                    sb.Append("[" + control.Text + "]");
                    sb.Append(" ");
                }
            }

            sb.AppendLine(string.Empty);

            return sb.ToString();
        }


        private void button_Click(object sender, EventArgs e)
        {
            TaskDialogResult = (int)((Button)sender).Tag;

            CloseForm();
        }

        /// <summary>
        /// Detect user pressing "ctrl + c".
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void EmulateTaskDialog_KeyDown(object sender, KeyEventArgs e)
        {
            const Keys notWanted = Keys.Alt | Keys.Shift;

            if ((e.Modifiers & notWanted) == 0)
            {
                if ((e.Modifiers & Keys.Control) == Keys.Control)
                {
                    if (e.KeyCode.Equals(Keys.C))
                    {
                        Clipboard.SetText(ToString());
                    }
                }
            }
        }
    }
}